<?php


namespace models\Business;

use Exception;
use models\Exception\Business\OrderException;
use Yaf\Registry;
use ylAlibaba\AliOpen;
use ylAlibaba\core\ylAlibabaException;

class Alibaba
{
    /**
     *  getProductListCybUserGroup以后的方法我都测试过了
     *  2020-05-13 黄旭
     */

    const APP_KEY      = "4911751";
    const APP_SECRET   = "9KxUA0WTFJA";
    const ACCESS_TOKEN = "478884a3-0e07-4585-affa-ac1384ef8a40";

    /**
     * 根据订单编号、运单编号获取物流信息
     * @param $order_id
     * @param $delivery_id
     * @return mixed
     * @throws ylAlibabaException
     */
    static public function getLogisticsTraceInfo($order_id, $delivery_id) {

        $ali = new AliOpen();
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

        // 测试时使用本地接口 正式接口调用时 注释掉下面两行

        $ali->params = [
            'orderId' => $order_id,
            'logisticsId' => $delivery_id,
            'webSite' => '1688'
        ];

        $return_arr = [];
        try {
            $resp = $ali->delivery->getLogisticsTraceInfo();
            foreach ($resp['logisticsTrace'][0]['logisticsSteps'] as $data)
                array_push($return_arr, ['date' => $data['acceptTime'], 'content' => $data['remark']]);

            return $return_arr;

        } catch (ylAlibabaException $e) {

        } finally {
            return $return_arr;
        }
    }

    /**
     * 通过1688订单号，获取运单号
     * @param string $order_id
     * @return mixed
     * @throws ylAlibabaException
     */
    static public function getDeliveryNo($order_id = '') {
        $ali = new AliOpen();
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

        // 测试时使用本地接口 正式接口调用时 注释掉下面两行

        $ali->params = [
            'orderId' => $order_id,
            'fields' => 'company,name,sendgood,receiver',
            'webSite' => '1688'
        ];

        try {
            $resp = $ali->delivery->getLogisticsInfos();

            return $resp;
        } catch (ylAlibabaException $e) {
            throw $e;
        }
    }

    /**
     * 上传退货退款凭证
     * @param string $order_sn
     * @param array $vouchers
     * @return array
     * @throws OrderException
     * @throws \models\Exception\DAO\ModelDriverException
     * @throws \models\Exception\DAO\ModelException
     * @throws \models\Exception\DAO\ModelReflectionException
     * @throws \models\Exception\DAO\ModelSqlException
     * @throws \models\Exception\Service\ServiceException
     * @throws \models\Exception\Transformer\TransformerException
     */
    static public function tradeUploadRefundVoucher($order_sn = '', $vouchers = [])
    {
        $return_arr = [];
        try {
            $ali = new AliOpen();
            $ali->setAppKey(static::APP_KEY);
            $ali->setAppSecret(static::APP_SECRET);

            // 测试时使用本地接口 正式接口调用时 注释掉下面两行
            //$ali->order->setMode('development');
            //$ali->order->res_url = Registry::get('config')['application']['host'] . "/common/alibabatest/tradeUploadRefundVoucher";

            foreach ($vouchers as $idx => $voucher) {
                //$file_path = str_replace(Registry::get('config')['application']['host'], '/Users/huangxu/PhpstormProjects/ylmall/public/', $voucher);
                $file_path = str_replace(Registry::get('config')['application']['host'], APP_PATH.'/public', $voucher);
                if (file_exists($file_path)) {
                    $ali->params = [
                        'imageData' => $file_path
                    ];
                    $resp = $ali->order->uploadRefundVoucher();

                    array_push($return_arr, $resp['imageDomain'] . '/' .$resp['imageRelativeUrl']);
                }
            }

            return $return_arr;
        } catch (ylAlibabaException $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            // 4、"上传凭证"接口调用失败，refund_stats为6(退货失败)，并日志记录失败原因，提示客户端"退货申请失败，请联系客服"
            $updatearr['refund_stats'] = 6;
            $updatearr['sn']           = $order_sn;
            Order::editOrder($updatearr);
            Registry::get('order_log')->err("退款+上传凭证接口调用失败,sn编号:" . $order_sn . ", 原因: " . $e);
            throw new OrderException(OrderException::ORDER_RETURN_FAILURE, OrderException::ORDER_RETURN_FAILURE_NO);

        } catch (Exception $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
        } finally {
            return $return_arr;
        }

    }

    /**
     * 发起退款、退货申请
     * @param $alibaba_order_id
     * @param $amount
     * @param $carriage
     * @param $reason_id
     * @param $description
     * @param $stats
     * @param array $vouchers
     * @return mixed
     * @throws ylAlibabaException
     */
    static public function getTradeCreateRefund($alibaba_order_id, $order_sn, $amount, $carriage, $reason_id, $description, $stats, $vouchers = [])
    {
        $ali = new AliOpen();
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

        // 根据"卖家未发货时允许退款；买家收到货后确认收货前允许退货"需求
        // 顾，允许退款的订单状态为1(未发货)对应1688的状态为"refundWaitSellerSend(售中等待买家发货)"
        // 允许退货的订单状态为2(已发货)对应1688的状态为"aftersaleBuyerReceived(售中等待买家收货)"
        switch ($stats) {
            // 售后已收到货 => refundWaitBuyerReceive(售中等待买家收货)
            case 2:
                $alibaba_stats = 'refundWaitBuyerReceive';
                $request       = 'returnRefund';
                break;
            // 待发货 => refundWaitSellerSend(售中等待卖家发货:)
            default;
                $alibaba_stats = 'refundWaitSellerSend';
                $request       = 'refund';
                break;
        }
        $ali->params = [
            'orderId'        => $alibaba_order_id,
            'orderEntryIds'  => json_encode([$alibaba_order_id]),
            'disputeRequest' => $request,
            'applyPayment'   => $amount,
            'applyCarriage'  => $carriage,
            'applyReasonId'  => $reason_id,
            'description'    => $description,
            'goodsStatus'    => $alibaba_stats,
        ];
        if(!empty($vouchers)){
             $ali->params['vouchers']=json_encode($vouchers);
        }
        // 测试时使用本地接口 正式接口调用时 注释掉下面两行
       // $ali->order->setMode('development');
        //$ali->order->res_url = Registry::get('config')['application']['host'] . "/common/alibabatest/tradeCreateRefund";

        try {
            $resp = $ali->order->tradeCreateRefund();
            return $resp['result'];
        } catch (ylAlibabaException $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            $updatearr['sn'] = $order_sn;
            if ($stats == 1) {
                $updatearr['refund_stats'] = 5;
                // 2、退款+接口调用失败，refund_stats为5(退款失败)，并日志记录失败原因，提示客户端"退款申请失败，请联系客服"
                Order::editOrder($updatearr);
                Registry::get('order_log')->err("退款+接口调用失败,sn编号:" . $order_sn . ", 原因: " . $e);
                throw new OrderException(OrderException::ORDER_REFUND_FAILURE, OrderException::ORDER_REFUND_FAILURE_NO);

            } else {
                // 3、退货+"退货接口"接口调用失败，refund_stats为6(退货失败)，并日志记录失败原因，提示客户端"退货申请失败，请联系客服"
                $updatearr['refund_stats'] = 6;
                Order::editOrder($updatearr);
                Registry::get('order_log')->err("退货+接口调用失败,sn编号:" . $order_sn . ", 原因: " . $e);
                throw new OrderException(OrderException::ORDER_RETURN_FAILURE, OrderException::ORDER_RETURN_FAILURE_NO);
            }
            throw $e;
        } catch (Exception $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw $e;
        }

    }

    /**
     * 获取1688退款理由
     * @param $alibaba_order_id
     * @param $stats
     * @return mixed
     * @throws ylAlibabaException
     */
    static public function getTradeRefundReasonList($alibaba_order_id, $stats)
    {
        $ali = new AliOpen();
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

        // 根据"卖家未发货时允许退款；买家收到货后确认收货前允许退货"需求
        // 顾，允许退款的订单状态为1(未发货)对应1688的状态为"refundWaitSellerSend(售中等待买家发货)"
        // 允许退货的订单状态为2(已发货)对应1688的状态为"aftersaleBuyerReceived(售中等待买家收货)"
        switch ($stats) {
            // 售后已收到货 => aftersaleBuyerReceived(售中等待买家收货)
            case 2:
                $alibaba_stats = 'refundWaitBuyerReceive';
                break;
            // 待发货 => refundWaitSellerSend(售中等待买家发货:)
            default;
                $alibaba_stats = 'refundWaitSellerSend';
                break;
        }
        $ali->params = [
            'orderId'       => $alibaba_order_id,
            'orderEntryIds' => json_encode([$alibaba_order_id]),
            'goodsStatus'   => $alibaba_stats
        ];
        // 测试时使用本地接口 正式接口调用时 注释掉下面两行
        //$ali->order->setMode('development');
        //$ali->order->res_url = Registry::get('config')['application']['host'] . "/common/alibabatest/tradeRefundReasonList";

        try {
            $resp = $ali->order->tradeRefundReasonList();
            return $resp['reasons'];
        } catch (ylAlibabaException $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw new Exception('退款理由信息获取失败，请联系客服', 400);
            throw $e;
        } catch (Exception $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw $e;
        }
    }

    /**
     * 查询退款单详情-根据退款单ID
     * @param $refundId
     * @return mixed
     * @throws Exception
     */
    static public function opQueryOrderRefund($refundId)
    {
        $ali = new AliOpen();
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

        $ali->params = [
            'refundId'       => $refundId,
        ];

        try {
            $resp = $ali->order->opQueryOrderRefund();
            return $resp;
        } catch (ylAlibabaException $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw new Exception('退款信息获取失败，请联系客服', 400);
            throw $e;
        } catch (Exception $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw $e;
        }
    }

    /**
     * 溢价模式创建订单前预览数据接口
     * @param $product_id
     * @param $num
     * @param $address_list
     * @param string $spec_id
     * @return mixed
     * @throws ylAlibabaException
     */
    static public function getCreateOrderPreview4CybMedia( $address_list, $cargoParamList)
    {
        if (empty($address_list) || !is_array($address_list))
            throw new ylAlibabaException("创建溢价订单前预览时收货地址必须为数据", 400);
        if (empty($cargoParamList) || !is_array($cargoParamList))
            throw new ylAlibabaException("创建溢价订单前预览时豆子有拼的下单信息必须为数据", 400);
        $ali = new AliOpen();
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

//        $product_list = [
//            'offerId'  => $product_id,
//            'quantity' => $num,
//        ];
//        if (!empty($spec_id))
//            $product_list['specId'] = $spec_id;

        $ali->params = [
            'addressParam'   => json_encode($address_list),
            'cargoParamList' => json_encode($cargoParamList)
        ];

        // 测试时使用本地接口 正式接口调用时 注释掉下面两行
        //$ali->order->setMode('development');
        //$ali->order->res_url = Registry::get('config')['application']['host'] . "/common/alibabatest/orderCreateOrderPreview4CybMedia";

        try {
            $resp = $ali->order->createOrderPreview4CybMedia()->getResp();
            // 日志记录溢价模式预览订单接口返回结果
            // some code...
            Registry::get('order_log')->info("创建订单前预览" . json_encode($resp));
            return $resp;
        } catch (ylAlibabaException $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw $e;
        } catch (Exception $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw $e;
        }

    }

    /**
     * 创建溢价订单
     * @param $product_id
     * @param $num
     * @param $spec_id
     * @param $address_list
     * @param $order_list
     * @return mixed
     * @throws ylAlibabaException
     */
    static public function getTradeCreateCybMedia($address_list,$cargoParamList, $order_list)
    {
        if (empty($address_list) || !is_array($address_list))
            throw new ylAlibabaException("创建溢价订单信息时收货地址必须为数据", 400);
        if (empty($order_list) || !is_array($order_list))
            throw new ylAlibabaException("创建溢价订单信息时豆子有拼的订单信息必须为数据", 400);
        if (empty($cargoParamList) || !is_array($cargoParamList))
            throw new ylAlibabaException("创建溢价订单信息时豆子有拼的下单信息必须为数据", 400);
        $ali = new AliOpen();
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

//        $product_list = [
//            'offerId'  => $product_id,
//            'quantity' => $num,
//        ];
//        if (!empty($spec_id))
//            $product_list['specId'] = $spec_id;

        $ali->params = [
            'addressParam'   => json_encode($address_list),
            'cargoParamList' => json_encode($cargoParamList),
            'message'        => '此处是给卖家的留言，一般情况下需要提醒卖家不得透露自己的相关信息，具体内容参考文档',
            'outerOrderInfo' => json_encode($order_list),
        ];
        // 测试时使用本地接口 正式接口调用时 注释掉下面两行
        //$ali->order->setMode('development');
        //$ali->order->res_url = Registry::get('config')['application']['host'] . "/common/alibabatest/tradeCreateOrder4CybMedia";

        try {
            $resp = $ali->order->tradeCreateOrder4CybMedia()->getResp();
            // 日志记录溢价模式预览订单接口返回结果
            // some code...
            Registry::get('order_log')->info("创建溢价订单" . json_encode($resp));
            // 返回1688信息（包括订单总金额，订单id,运费），用以支付宝协议支付使用
            return $resp['result'];

        } catch (ylAlibabaException $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw $e;
        } catch (Exception $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw $e;
        }
    }

    /**
     * 1688使用支付宝协议支付对指定订单进行付款
     * @param $alibaba_order_id
     * @throws ylAlibabaException
     */
    static public function tradePayProtocolPay($alibaba_order_id)
    {
        if (empty($alibaba_order_id))
            throw new ylAlibabaException("1688订单编号不能为空", 400);

        $ali = new AliOpen();
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

        $ali->params = ['orderId' => $alibaba_order_id];

        // 测试时使用本地接口 正式接口调用时 注释掉下面两行
//        $ali->order->setMode('development');
//        $ali->order->res_url = Registry::get('config')['application']['host'] . "/common/alibabatest/tradePayProtocolPay";

        try {
            $resp = $ali->order->tradePayProtocalPay()->getResp();
            // 日志记录协议支付订单接口返回结果
            Registry::get('alibaba_log')->info('支付宝协议支付结果:' . json_encode($resp));

        } catch (ylAlibabaException $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw $e;
        } catch (Exception $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw $e;
        }
    }

    /**
     * 获取我的选品库列表
     * @param $page
     * @param int $page_size
     * @return mixed
     */
    static public function getProductListCybUserGroup($page, $page_size = 10)
    {
        $ali         = new AliOpen();
        $ali->params = ['pageNo' => $page, 'pageSize' => $page_size];
        if (!empty($feed_id))
            array_push($ali->params, ['feedId' => $feed_id]);
        if (!empty($title))
            array_push($ali->params, ['title' => $title]);
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

        // 测试时使用本地接口 正式接口调用时 注释掉下面两行
//        $ali->product->setMode('development');
//        $ali->product->res_url = Registry::get('config')['application']['host']."/common/alibabatest/listCybUserGroup";

        return $ali->product->productListCybUserGroup()->getResp();
    }

    /**
     * 获取指定选品库下的商品信息
     * @param $group_id
     * @param $page
     * @param int $page_size
     * @param string $feed_id
     * @param string $title
     * @return array
     */
    static public function getProductListCybUserGroupFeed($group_id, $page, $page_size = 10, $feed_id = '', $title = '')
    {
        $ali         = new AliOpen();
        $ali->params = ['groupId' => $group_id,
                        'pageNo'  => $page, 'pageSize' => $page_size];
        if (!empty($feed_id))
            $ali->params['feedId'] = $feed_id;
        if (!empty($title))
            $ali->params['title'] = $title;

        var_dump($ali->params);
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

        // 测试时使用本地接口 正式接口调用时 注释掉下面两行
//        $ali->product->setMode('development');
//        $ali->product->res_url = Registry::get('config')['application']['host']."/common/alibabatest/listCybUserGroupFeed";

        $resp       = $ali->product->productListCybUserGroupFeed()->getResp();
        $return_arr = [];
        foreach ($resp['result']['resultList'] as $val) {
            if ($val['invalid'] === false) {
                array_push($return_arr, [
                    'product_id'      => $val['feedId'],
                    'title'           => $val['title'],
                    'price'           => $val['price'],
                    'promotion_price' => isset($val['promotionPrice']) ? $val['promotionPrice'] : 0.00,
                    'img_url'         => $val['imgUrl'],
                    'sales'           => isset($val['saleCount']) ? $val['saleCount'] : 0,
                    'channel_price'   => isset($val['channelPrice']) ? $val['channelPrice'] : 0.00
                ]);
            }
        }

        return $return_arr;
    }

    /**
     * 解析1688返回的description中的html，只保留图片的src
     * @param string $description
     * @return string
     */
    static public function parseHtml($description = '') {
        $html = '';
        if (!empty($description)) {
            // 替换调所有关联商品推荐、掌柜推荐
            $description = preg_replace("/SHOPTOOL_POSITION_TOP_BEGIN(.*)SHOPTOOL_POSITION_TOP_END/Usi", '', $description);

            if (preg_match_all('/src="(.*)"/Usi', $description, $arr)) {
                foreach ($arr[1] as $src) {
                    $html .= '<p style="margin: 0px; padding: 0px;"><img src="' . $src . '" /></p>';
                }
            }
        }

        return $html;

    }

    /**
     * 获取产品详情
     * @param $product_id
     * @return mixed
     */
    static public function getProductCpsMediaProductInfo($product_id)
    {
        $ali         = new AliOpen();
        $ali->params = ['offerId' => $product_id, 'needCpsSuggestPrice' => true];
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

        // 测试时使用本地接口 正式接口调用时 注释掉下面两行
//        $ali->product->setMode('development');
//        $ali->product->res_url = "http://www.ylmall.com/common/alibabatest/productInfo";

        $product_information = $ali->product->productCpsMediaProductInfo();

        $product_arr = $sku_arr = $product_sku_arr = $shipping_arr = $attribute_arr = $match_sku_id_arr = [];
        if ($product_information['productInfo']['productID'] == $product_id
            && in_array($product_information['productInfo']['status'], ['published', 'modified', 'new', 'approved'])
            /*&& $product_information['productInfo']['saleInfo']['supportOnlineTrade'] == 'true'*/
        ) {
            // 类目ID，标识商品所属类目
            $product_arr['category_id'] = $product_information['productInfo']['categoryID'];
            // 分组ID，确定商品所属分组。1688可传入多个分组ID，国际站同一个商品只能属于一个分组，因此默认只取第一个
            $product_arr['group_id'] = isset($product_information['productInfo']['groupID']) ? $product_information['productInfo']['groupID'][0] : 0;
            // 商品标题，最多128个字符
            $product_arr['title'] = $product_information['productInfo']['subject'];
            // 商品详情描述，可包含图片中心的图片URL
            $product_arr['description'] = self::parseHtml($product_information['productInfo']['description']); /*$product_information['productInfo']['description'];*/
            // pictureAuth为图片是否私密字段
            // 若pictureAuth为false时，读取images
            // images为主图列表，使用相对路径，需要增加域名：https://cbu01.alicdn.com/
            if ($product_information['productInfo']['pictureAuth'] === false) {
                foreach ($product_information['productInfo']['image']['images'] as $idx => $url) {
                    $product_information['productInfo']['image']['images'][$idx] = /*'https://cbu01.alicdn.com/' .*/
                        $url;
                }
                $product_arr['images'] = implode(',', $product_information['productInfo']['image']['images']);
            } else
                $product_arr['images'] = '';
            // 质量星级(0-5)
            $product_arr['quality_level'] = isset($product_information['productInfo']['qualityLevel']) ? $product_information['productInfo']['qualityLevel'] : 0;
            // 供应商loginId
            $product_arr['supplier_login_id'] = isset($product_information['productInfo']['supplierLoginId']) ? $product_information['productInfo']['supplierLoginId'] : '';
            // 类目名
//            $product_arr['category_name'] = $product_information['productInfo']['categoryName'];
            // 主图视频播放地址 绝对路径
            $product_arr['main_video'] = isset($product_information['productInfo']['mainVedio']) ? $product_information['productInfo']['mainVedio'] : '';
            // 商品货号，产品属性中的货号
            $product_arr['product_cargo_number'] = isset($product_information['productInfo']['productCargoNumber']) ? $product_information['productInfo']['productCargoNumber'] : '';
            // 参考价格，返回价格区间，可能为空
            $product_arr['reference_price'] = $product_information['productInfo']['referencePrice'];
            // 商品状态。
            // published:上网状态;
            // member expired:会员撤销;
            // auto expired:自然过期;
            // expired:过期(包含手动过期与自动过期);
            // member deleted:会员删除;
            // modified:修改;
            // new:新发;
            // deleted:删除;
            // TBD:to be delete;
            // approved:审批通过;
            // auditing:审核中;
            // untread:审核不通过;
            $product_arr['alibaba_stats'] = $product_information['productInfo']['status'];

            // 商品销售信息
            if (isset($product_information['productInfo']['saleInfo']) && !empty($product_information['productInfo']['saleInfo'])
            ) {
                $product_arr['product_id'] = $product_information['productInfo']['productID'];
                // 是否支持网上交易。true：支持 false：不支持
                $product_arr['support_online_trade'] = $product_information['productInfo']['saleInfo']['supportOnlineTrade'] == 'true' ? 1 : 0;
                // 是否支持混批
                $product_arr['mix_whole_sale'] = $product_information['productInfo']['saleInfo']['mixWholeSale'] == 'true' ? 1 : 0;
                // 是否价格私密信息
                $product_arr['price_auth'] = $product_information['productInfo']['saleInfo']['priceAuth'] == 'true' ? 1 : 0;
                if ($product_arr['price_auth'] == 0) {
                    $product_arr['price_ranges'] = json_encode($product_information['productInfo']['saleInfo']['priceRanges']);
                }
                // 可售数量
                $product_arr['amount_on_sale'] = $product_information['productInfo']['saleInfo']['amountOnSale'];
                // 计量单位
                $product_arr['unit'] = $product_information['productInfo']['saleInfo']['unit'];
                // 最小起订量，范围是1-99999。
                $product_arr['min_order_quantity'] = $product_information['productInfo']['saleInfo']['minOrderQuantity'];
                // 每批数量，默认为空或者非零值，该属性不为空时sellunit为必填
                $product_arr['batch_number'] = isset($product_information['productInfo']['saleInfo']['batchNumber']) ?
                    $product_information['productInfo']['saleInfo']['batchNumber'] : 0;
                // 售卖单位，如果为批量售卖，代表售卖的单位，该属性不为空时batchNumber为必填，例如1"手"=12“件"的"手"
                $product_arr['sell_unit'] = isset($product_information['productInfo']['saleInfo']['sellunit']) ?
                    $product_information['productInfo']['saleInfo']['sellunit'] : '';
                // 0-无SKU按数量报价,1-有SKU按规格报价,2-有SKU按数量报价
                $product_arr['quote_type'] = $product_information['productInfo']['saleInfo']['quoteType'];
                // 分销基准价。代销场景均使用该价格。有SKU商品查看skuInfo中的consignPrice
                $product_arr['consign_price'] = isset($product_information['productInfo']['saleInfo']['consignPrice']) ?
                    $product_information['productInfo']['saleInfo']['consignPrice'] : '0.00';
                // CPS建议价（单位：元)
                $product_arr['cps_suggest_price'] = isset($product_information['productInfo']['saleInfo']['cpsSuggestPrice']) ?
                    $product_information['productInfo']['saleInfo']['cpsSuggestPrice'] : '0.00';
                //厂货通渠道专享价（单位：元）
                $product_arr['channel_price'] = isset($product_information['productInfo']['saleInfo']['channelPrice']) ?
                    $product_information['productInfo']['saleInfo']['channelPrice'] : '0.00';
            }

            // 商品物流信息
            if (isset($product_information['productInfo']['shippingInfo']) && !empty($product_information['productInfo']['shippingInfo'])
            ) {
                // 商品运费费率
                if (isset($product_information['productInfo']['shippingInfo']['freightTemplate']) &&
                    !empty($product_information['productInfo']['shippingInfo']['freightTemplate'])
                ) {
                    foreach ($product_information['productInfo']['shippingInfo']['freightTemplate'] as $f) {
                        $addressCodeText = is_null($f['addressCodeText']) ? '' : $f['addressCodeText'];

                        foreach ($f['expressSubTemplate']['rateList'] as $rate) {
                            array_push($shipping_arr, [
                                'product_id'                      => $product_information['productInfo']['productID'],
                                'charge_type' => is_null($f['expressSubTemplate']['subTemplateDTO']['chargeType']) ? 0 :
                                    $f['expressSubTemplate']['subTemplateDTO']['chargeType'],
                                'unit_weight'                     => isset($product_information['productInfo']['shippingInfo']['unitWeight']) ?
                                    $product_information['productInfo']['shippingInfo']['unitWeight'] : 0.00,
                                'send_goods_address_id'           => $product_information['productInfo']['shippingInfo']['sendGoodsAddressId'],
                                'send_goods_address_text'         => $addressCodeText,
                                'suttle_weight'                   => isset($product_information['shippingInfo']['suttleWeight']) ? $product_information['shippingInfo']['suttleWeight'] : 0.00,
                                'width'                           => isset($product_information['shippingInfo']['width']) ? $product_information['shippingInfo']['width'] : 0.00,
                                'height'                          => isset($product_information['shippingInfo']['height']) ? $product_information['shippingInfo']['height'] : 0.00,
                                'length'                          => isset($product_information['shippingInfo']['length']) ? $product_information['shippingInfo']['length'] : 0.00,
                                'to_area_code_text'               => $rate['toAreaCodeText'],
                                'first_unit'                      => $rate['rateDTO']['firstUnit'],
                                'first_unit_fee'                  => $rate['rateDTO']['firstUnitFee'],
                                'next_unit'                       => $rate['rateDTO']['nextUnit'],
                                'next_unit_fee'                   => $rate['rateDTO']['nextUnitFee'],
                                'channel_price_free_postage'      => isset($product_information['channelPriceFreePostage']) ? $product_information['channelPriceFreePostage'] : 0.00,
                                'channel_price_exclude_areaCodes' => isset($product_information['shippingInfo']['channelPriceExcludeAreaCodes']) ?
                                    json_encode($product_information['shippingInfo']['channelPriceExcludeAreaCodes']) : ''
                            ]);
                        }
                    }
                } else if (isset($product_information['productInfo']['shippingInfo']['freightTemplateID']) &&
                    $product_information['productInfo']['shippingInfo']['freightTemplateID'] == 1) {
                    $shipping_arr[] = [
                        'product_id'  => $product_information['productInfo']['productID'],
                        'charge_type' => 0,
                        'unit_weight' => isset($product_information['productInfo']['shippingInfo']['unitWeight']) ?
                            $product_information['productInfo']['shippingInfo']['unitWeight'] : 0.00,
                        'send_goods_address_id' => $product_information['productInfo']['shippingInfo']['sendGoodsAddressId'],
                        'send_goods_address_text' => $product_information['productInfo']['shippingInfo']['sendGoodsAddressText'],
                        'suttle_weight' =>  0.00,
                        'width' => 0.00,
                        'height' => 0.00,
                        'length' => 0.00,
                        'to_area_code_text' => 0,
                        'first_unit' => isset($product_information['productInfo']['shippingInfo']['unitWeight']) ?
                            $product_information['productInfo']['shippingInfo']['unitWeight'] : 0.00,
                        'first_unit_fee' => 0.00,
                        'next_unit' => 0,
                        'next_unit_fee' => 0.00,
                        'channel_price_free_postage' => 0.00,
                        'channel_price_exclude_areaCodes' => ''
                    ];
                }
            }

            // 产品属性列表
            if (isset($product_information['productInfo']['attributes']) &&
                !empty($product_information['productInfo']['attributes'])
            ) {
                foreach ($product_information['productInfo']['attributes'] as $attributes_info) {
                    array_push($attribute_arr, [
//                        'product_id' => $product_information['productInfo']['productID'],
'attribute_id'   => $attributes_info['attributeID'],
'attribute_name' => $attributes_info['attributeName'],
'value_id'       => isset($attributes_info['valueID']) ? $attributes_info['valueID'] : 0,
'value'          => isset($attributes_info['value']) ? str_replace('\'', '\\\'',  $attributes_info['value']) : '',
//                        'is_custom' => $attributes_info['isCustom'] === true ? 1 : 0,
                    ]);
                }
            }


            // sku信息
            if (isset($product_information['productInfo']['skuInfos']) && !empty($product_information['productInfo']['skuInfos'])) {
                $min_consign_price = $product_arr['consign_price'];
                foreach ($product_information['productInfo']['skuInfos'] as $sku_info) {
                    $min_consign_price = $min_consign_price == 0 ? $sku_info['consignPrice'] :
                        ($min_consign_price > $sku_info['consignPrice'] ? $sku_info['consignPrice'] : $min_consign_price);
                    // 商品与sku对应信息表的数据
                    array_push($product_sku_arr, [
                        // skuId,该规格在所有商品中的唯一标记
                        'sku_id'         => $sku_info['skuId'],
                        // 商品编号
                        'product_id'     => $product_id,
                        // specId,该规格在本商品内的唯一标记
                        'spec_id'        => $sku_info['specId'],
                        // 指定规格的货号
                        'cargo_number'   => $sku_info['cargoNumber'],
                        // 可销售数量
                        'amount_on_sale' => $sku_info['amountOnSale'],
                        // 建议零售价
                        'retail_price'   => isset($sku_info['retailPrice']) ? $sku_info['retailPrice'] : 0.00,
                        // 报价时该规格的单价
                        'price'          => isset($sku_info['price']) ? $sku_info['price'] : 0.00,
                        // 分销基准价。代销场景均使用该价格。无SKU商品查看saleInfo中的consignPrice
                        'consign_price'  => isset($sku_info['consignPrice']) ? $sku_info['consignPrice'] : 0.00,
                        // attribute_id
                        'attribute_id'   => implode(',', array_column($sku_info['attributes'], 'attributeValue')),
                        // sku图片名
                        'sku_image_url'  => !empty(array_column($sku_info['attributes'], 'skuImageUrl')) ?
                            array_column($sku_info['attributes'], 'skuImageUrl')[0] : ''
                    ]);

                    // sku信息
                    foreach ($sku_info['attributes'] as $attribute) {
                        if (empty($match_sku_id_arr) || (!empty($match_sku_id_arr) && !in_array($attribute['attributeValue'], $match_sku_id_arr))) {
                            array_push($sku_arr, [
                                // sku属性ID
                                'attribute_id'           => $attribute['attributeID'],
                                // sku值内容
                                'attribute_value'        => str_replace('\'', '\\\'',  $attribute['attributeValue']),
                                // sku属性ID所对应的显示名，比如颜色，尺码
                                'attribute_display_name' => isset($attribute['attributeDisplayName']) ? $attribute['attributeDisplayName'] : '',
                                // sku属性ID所对应的显示名，比如颜色，尺码
                                'attribute_name'         => $attribute['attributeName'],
                            ]);
                            array_push($match_sku_id_arr, $attribute['attributeValue']);
                        }
                    }
                }
                $product_arr['consign_price'] = $min_consign_price;
            }
        }

        return [$product_arr, $sku_arr, $product_sku_arr, $shipping_arr, $attribute_arr];

    }

    /**
     * 获取商品活动详情
     * @param $product_id
     * @return array
     */
    static public function getProductQueryOfferDetailActivity($product_id)
    {
        $ali         = new AliOpen();
        $ali->params = ['offerId' => $product_id, 'needCpsSuggestPrice' => true];
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

        // 测试时使用本地接口 正式接口调用时 注释掉下面两行
//        $ali->product->setMode('development');
//        $ali->product->res_url = Registry::get('config')['application']['host']."/common/alibabatest/queryOfferDetailActivity";

        $resp          = $ali->product->productQueryOfferDetailActivity();
        $promotion_arr = $promotion_product_arr = [];
        if ($resp['result']['offerId'] == $product_id) {
            list($datetime, $hour) = explode('+', $resp['result']['startTime']);
            $datetime   = strlen($datetime) == 14 ? strtotime($datetime) : strtotime(substr($datetime, 0, 14));
            $start_time = date("Y-m-d H:i:s", $datetime);

            if (isset($resp['result']['hotTime']) && !empty($resp['result']['hotTime'])) {
                list($datetime, $hour) = explode('+', $resp['result']['hotTime']);
                $datetime = strlen($datetime) == 14 ? strtotime($datetime) : strtotime(substr($datetime, 0, 14));
                $hot_time = date("Y-m-d H:i:s", $datetime);
            } else {
                $hot_time = '0000-00-00 00:00:00';
            }

            list($datetime, $hour) = explode('+', $resp['result']['endTime']);
            $datetime = strlen($datetime) == 14 ? strtotime($datetime) : strtotime(substr($datetime, 0, 14));
            $end_time = date("Y-m-d H:i:s", $datetime);

            list($datetime, $hour) = explode('+', $resp['result']['freepostageStartTime']);
            $datetime                = strlen($datetime) == 14 ? strtotime($datetime) : strtotime(substr($datetime, 0, 14));
            $free_postage_start_time = date("Y-m-d H:i:s", $datetime);

            list($datetime, $hour) = explode('+', $resp['result']['freepostageEndTime']);
            $datetime              = strlen($datetime) == 14 ? strtotime($datetime) : strtotime(substr($datetime, 0, 14));
            $free_postage_end_time = date("Y-m-d H:i:s", $datetime);

            $promotion_product_arr['product_id']  = $resp['result']['offerId'];
            $promotion_product_arr['activity_id'] = $promotion_arr['activity_id'] = $resp['result']['activityId'];
            $promotion_arr['activity_name']       = $resp['result']['activityName'];
            // 预热时间,活动未开始,不可用活动价下单; 为null表示无预热时间
            $promotion_arr['hot_time'] = $hot_time;
            // 活动开始时间；大于now时，活动有效
            $promotion_arr['start_time'] = $start_time;
            // 活动结束时间；小于now时，活动有效
            $promotion_arr['end_time'] = $end_time;
            // 活动起批量
            $promotion_product_arr['begin_quantity'] = isset($resp['result']['beginQuantity']) ? $resp['result']['beginQuantity'] : 0;
            // 活动总库存，为null时使用offer原库存
            $promotion_product_arr['stock'] = isset($resp['result']['stock']) ? $resp['result']['stock'] : 0;
            // 商品本身限购数，非活动价可购买数；-1表示不限，0表示可购买数为0；3个*LimitCount字段都等于-1时，表示没有任何限购
            $promotion_product_arr['person_limit_count'] = $resp['result']['personLimitCount'];
            // 限购数，等于0且personLimitCount>0时，可以以原价下单，但不能以活动价下单；-1表示不限数量；3个*LimitCount字段都等于-1时，表示没有任何限购
            $promotion_product_arr['promotion_limit_count'] = $resp['result']['promotionLimitCount'];
            // 活动限购数；该场内活动商品限购数，-1表示不限购；0表示不可购买该场活动所有商品；3个*LimitCount字段都等于-1时，表示没有任何限购
            $promotion_product_arr['activity_limit_count'] = $resp['result']['activityLimitCount'];
            // 活动限时包邮开始时间；null 表示不限时
            $promotion_product_arr['free_postage_start_time'] = $free_postage_start_time;
            // 活动限时包邮结束时间；null 表示不限时
            $promotion_product_arr['free_postage_end_time'] = $free_postage_end_time;
            // 免包邮地区，与活动包邮配合使用
            $promotion_product_arr['exclude_area_list'] = isset($resp['result']['excludeAreaList']) ? json_encode($resp['result']['excludeAreaList']) : '';
            // 如果offer是范围报价，且价格优惠是折扣的情况，返回折扣计算后的价格范围;优先取该字段，该字段为空时，表示分sku报价，取promotionItemList
            $promotion_product_arr['range_price'] = isset($resp['result']['rangePrice']) ? json_encode($resp['result']['rangePrice']) : '';
            // 优惠结果，根据优惠方式（PromotionInfo），结合offer的原价信息，计算出优惠结果：每个sku或者每个区间价的促销价，折扣率
            $promotion_product_arr['promotion_item_list'] = isset($resp['result']['promotionItemList']) ? json_encode($resp['result']['promotionItemList']) : '';
            // sku维度的库存结果
            $promotion_product_arr['sku_stock_list']   = isset($resp['result']['skuStockList']) ? json_encode($resp['result']['skuStockList']) : '';
            $promotion_product_arr['intro_order_flow'] = $resp['result']['introOrderFlow'];
        }

        return [$promotion_arr, $promotion_product_arr];
    }

    /**
     * 调用1688获取商品分类接口
     * @param $category_id
     * @return array
     */
    static public function getCategoryFrom1688Api($category_id)
    {
        $ali         = new AliOpen();
        $ali->params = ['categoryID' => $category_id];
        $ali->setAppkey(self::APP_KEY);
        $ali->setAppsecret(self::APP_SECRET);
//        $ali->access_token = self::ACCESS_TOKEN;
        return $ali->category->categoryGet()->getResp();
    }

    /**
     * 调用1688接口，获取指定分类的所有下级分类
     * @param int $category_id
     * @param array $parent_result
     * @return array|mixed
     */
    static public function getSubCategoryById($category_id = 0, $parent_result = [])
    {
        list($sub_id_arr, $result) = self::getCategoryFrom1688Api($category_id);

        if (!empty($result)) {
            if (!empty($parent_result)) {
                foreach ($parent_result as $idx => $category_info) {
                    if ($category_info['id'] == $result[0]['id']) {
                        unset($parent_result[$idx]);
                    }
                }
            }
            $result = !empty($parent_result) ? array_merge($parent_result, $result) : $result;
        }

        // 还有叶级分类需要获取
        if (!empty($sub_id_arr)) {
            foreach ($sub_id_arr as $idx => $sub_id)
                $result = self::getSubCategoryById($sub_id, $result);
        }

        return $result;
    }

    /**
     * 关注指定商品
     * @param int $product_id
     * @return bool
     * @throws ylAlibabaException
     */
    static public function getProductFollow($product_id = 0)
    {

        $ali         = new AliOpen();
        $ali->params = ['productId' => $product_id];
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

        // 测试时使用本地接口 正式接口调用时 注释掉下面两行
//        $ali->product->setMode('development');
//        $ali->product->res_url = Registry::get('config')['application']['host'] . "/common/alibabatest/follow";
        try {
            $resp = $ali->product->productFollow();
            // 日志记录$resp
            return $resp['message'] === 'success';
        } catch (ylAlibabaException $e) {
            throw $e;
        }
    }

    /**
     * 取关指定商品
     * @param int $product_id
     * @return bool
     * @throws ylAlibabaException
     */
    static public function unFollowProduct($product_id = 0)
    {

        $ali         = new AliOpen();
        $ali->params = ['productId' => $product_id];
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

        // 测试时使用本地接口 正式接口调用时 注释掉下面两行
//        $ali->product->setMode('development');
//        $ali->product->res_url = Registry::get('config')['application']['host'] . "/common/alibabatest/follow";

        try {
            $resp = $ali->product->productUnFollow();
            // 日志记录$resp
            return $resp['message'] === 'success';
        } catch (ylAlibabaException $e) {
            throw $e;
        }
    }


    /**
     * 买家提交退款货信息 在1688卖家同意退货后调用
     * @param $refund_order_id
     * @param $logistics_company_no
     * @param $freight_bill
     * @param string $description
     * @param array $vouchers
     * @return mixed
     * @throws ylAlibabaException
     */
    static public function tradeRefundReturnGoods($refund_order_id, $logistics_company_no, $freight_bill, $description = '', $vouchers = [])
    {
        $ali = new AliOpen();
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

        // 测试时使用本地接口 正式接口调用时 注释掉下面两行
//        $ali->order->setMode('development');
//        $ali->order->res_url = Registry::get('config')['application']['host'] . "/common/alibabatest/tradeRefundReturnGoods";

        $ali->params = [
            'refundId'           => $refund_order_id,
            'logisticsCompanyNo' => $logistics_company_no,
            'freightBill'        => $freight_bill,
            'description'        => $description,
        ];
        if(!empty($vouchers)){
            $ali->param['vouchers']=json_decode($vouchers);
        }
        try {
            $resp = $ali->order->tradeRefundReturnGoods();
            return $resp['result'];
        } catch (ylAlibabaException $e) {
            $message=explode('-',$e->getMessage());
            if(isset($message[2])){
                $message=$message[2];
            }else{
                $message=$e->getMessage();
            }
            throw new ylAlibabaException($message, '400');
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            //throw $e;
        } catch (Exception $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw $e;
        }
    }

    /**
     * 获取所有的物流公司
     * @return mixed
     * @throws Exception
     */
    static public function getLogisticsOpQueryLogisticCompanyListOffline()
    {
        $ali = new AliOpen();
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

        // 测试时使用本地接口 正式接口调用时 注释掉下面两行
        $ali->order->setMode('development');
        $ali->order->res_url = Registry::get('config')['application']['host'] . "/common/alibabatest/logisticsOpQueryLogisticCompanyListOffline";

        try {

            $resp = $ali->order->logisticsOpQueryLogisticCompanyList()->getResp();
            return $resp['result'];
        } catch (ylAlibabaException $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw new Exception('物流公司信息获取失败，请联系客服', 400);
        } catch (Exception $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw $e;
        }
    }

    /**
     * 根据地址解析地区码
     * @param $address_id
     * @return array
     * @throws ylAlibabaException
     */
    static public function getTradeAddressCodeParse($address_id)
    {
        // 查询数据库 address_id是否存在,并将地址生成"xxx省xxx市xxx区xxx街道xxx号"的形式
        // 测试用
        //$address = '浙江省杭州市滨江区网商路699号';
        $addinfo = Address::getAddress(['id' => $address_id]);
        $address = $addinfo['province'] . $addinfo['city'] . $addinfo['area'] . $addinfo['street'] . $addinfo['location'];

        $ali = new AliOpen();
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

        $ali->params = [
            'addressInfo' => $address
        ];
        // 测试时使用本地接口 正式接口调用时 注释掉下面两行
        //$ali->order->setMode('development');
        //$ali->order->res_url = Registry::get('config')['application']['host'] . "/common/alibabatest/orderTradeAddressCodeParse";

        try {
            $address_info = $ali->order->tradeAddressCodeParse();
            // 日志记录地址解析地区码接口返回结果
            // some code...

            $post_code     = $address_info['postCode'];
            $district_code = $address_info['addressCode'];

            return [
                //'addressId'    => $address_id,
                // 收货人手机号，address表中获取
                'mobile'       => $addinfo['mobile'],
                'phone'        => $addinfo['mobile'],
                'fullName'     => $addinfo['name'],
                'postCode'     => $post_code,
                // 城市中文名称，address表中获取
                'cityText'     => $addinfo['city'],
                // 省份中文名称，address表中获取
                'provinceText' => $addinfo['province'],
                // 区县中文名称，address表中获取
                'areaText'     => $addinfo['area'],
                // 镇中文名称，默认为空
                'townText'     => '',
                // 详细地址，address中的street+location
                'address'      => $addinfo['street'] . $addinfo['location'],
                'districtCode' => $district_code
            ];

        } catch (ylAlibabaException $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw $e;
        } catch (Exception $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw $e;
        }
    }

    /**
     * 获取所有的物流公司
     * @return mixed
     * @throws ylAlibabaException
     */
    static public function getLogisticsOpQueryLogisticCompanyList()
    {
        $ali = new AliOpen();
        $ali->setAppKey(static::APP_KEY);
        $ali->setAppSecret(static::APP_SECRET);

        // 测试时使用本地接口 正式接口调用时 注释掉下面两行
//        $ali->order->setMode('development');
//        $ali->order->res_url = Registry::get('config')['application']['host'] . "/common/alibabatest/logisticsOpQueryLogisticCompanyList";

        try {

            $resp = $ali->order->logisticsOpQueryLogisticCompanyList()->getResp();
            return $resp['result'];
        } catch (ylAlibabaException $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw $e;
        } catch (Exception $e) {
            // 若接口抛错，记录下错误信息并抛出错误，发送通知消息给用户，告知下单失败请联系客服
            throw $e;
        }
    }
}
